前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket

# session登录态判断处理

30 秒速记

  • session = 服务端存用户状态,浏览器 Cookie 里只放一个 session id,每次请求带上它来认人
  • 有 Cookie 不等于已登录:要拿 id 去存储里查到会话、没过期、里面有 userId
  • 登录态校验写成中间件,通过后把用户放进 ctx.state.user,后面直接用
  • 公开页、可选登录页、必须登录页分开挂,别全局一刀切
  • 页面没登录 → 跳登录页;API 没登录 → 401,登录了但没权限 → 403

session 登录态的原理是:用户状态存在服务端,浏览器只拿一个 session id 放在 Cookie 里,每次请求服务端拿这个 id 去查是谁。 判断登录态时只看浏览器有没有这个 Cookie 是不够的,要真的去存储里查到会话,确认没过期、有 userId,比较严格的场景还要确认账号没被禁用。这段逻辑我会写成一个中间件,只挂在需要登录的路由上,通过后把用户信息放进 ctx.state.user;页面请求失败就跳登录页,接口请求失败就返回 401。

理解「session登录态判断处理」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 security 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

HTTP 本身是无状态的,服务端分不清两次请求是不是同一个人发的。session 的思路是让服务端记账:用户登录成功后,服务端生成一个随机的 session id,把「这个 id 对应用户 42,已登录」存进内存、MySQL 或者 Redis,再通过 Set-Cookie 把 id 发给浏览器。之后浏览器每次请求自动带上这个 Cookie,服务端拿 id 一查就知道是谁。

一个能用的登录校验中间件大概长这样:

// middlewares/requireLogin.js
module.exports = function requireLogin({ api = false } = {}) {
  return async (ctx, next) => {
    const s = ctx.session
    if (!s || !s.isLogin || !s.userId) {
      if (api) {
        ctx.status = 401
        ctx.body = { success: false, code: 'NOT_LOGIN' }
      } else {
        ctx.redirect('/login?returnTo=' + encodeURIComponent(ctx.path))
      }
      return
    }
    ctx.state.user = { id: s.userId, name: s.userName }
    await next()
  }
}

// 只挂在需要登录的路由上
router.get('/work', requireLogin(), pageCtrl.work)
router.get('/user/getUserInfo.json', requireLogin({ api: true }), userCtrl.getLoginUserInfo)
@前端进阶之旅: 代码已经复制到剪贴板

几个容易忽略的点:session 中间件要先于这个中间件注册,否则 ctx.session 还没恢复;Cookie 要设 httpOnly,防止被页面脚本读走;returnTo 跳回时只允许站内路径,不然就是开放重定向漏洞。

面试官追问

追问 1只要请求带了 USER_SID 这个 Cookie 就认为已登录,有什么问题?
参考回答

Cookie 是客户端随便能改的,随便填一个值也能带上。必须拿这个 id 去存储里查,查不到、过期了、里面没有 userId 都算没登录。

追问 2用户改了密码,其他设备上的旧登录要立刻失效,session 方案怎么做?
参考回答

在用户表里加一个 tokenVersion,写 session 时一起存进去,校验中间件每次比对数据库里的版本,对不上就踢掉。或者改密码时直接把这个用户所有会话从存储里删掉,前提是存储能按 userId 查会话。

追问 3session 和 JWT 怎么选?
参考回答

session 状态在服务端,踢人、改权限立刻生效,代价是要有共享存储;JWT 无状态、扩展方便,但签发出去就收不回来,只能靠短有效期加刷新令牌。传统网站、后台系统我一般用 session,开放给第三方或者跨很多服务的场景再考虑 JWT。

追问 4App 客户端存不住 Cookie,还能用这套 session 吗?
参考回答

可以,把 session id 放进请求头比如 Authorization: Bearer <sid>,服务端从头里取再去存储查,逻辑不变。要注意头和 Cookie 两个来源别混着判断,同一个接口只认一种。

追问 5登录后一刷新就掉线,按什么顺序查?
参考回答

先看登录响应有没有 Set-Cookie,再看刷新请求有没有带上这个 Cookie,最后看服务端拿这个 id 能不能查到会话。三步哪步断了就在哪查:没下发看中间件顺序,没带上看 Domain、Path、SameSite、Secure,查不到看存储。

# session登录态判断处理

# 使用session中间件

30 秒速记

  • koa-session-minimal 提供 ctx.session,koa-mysql-session 把会话数据存进 MySQL
  • key: 'USER_SID' 是 Cookie 名,Cookie 里只有 session id,用户数据在 MySQL 里
  • session 中间件必须在所有读写 ctx.session 的中间件和路由之前注册
  • 多实例部署要共享存储(MySQL / Redis),存内存的话请求打到另一台就掉线
  • Cookie 记得配 httpOnly、sameSite,HTTPS 下加 secure

这段配置做的事是:用 koa-session-minimal 给每个请求挂上 ctx.session,用 koa-mysql-session 把会话内容存到 MySQL,浏览器 Cookie 里只放一个叫 USER_SID 的 id。 存储放在 MySQL 而不是进程内存,是为了服务重启或者多台机器部署时登录态不丢。注册顺序很关键,session 中间件要放在路由前面,后面的代码才拿得到恢复好的 ctx.session。实际项目里 session 我更多放 Redis,读写快、自带过期,MySQL 存也能用,就是要定期清理过期记录。

// code ...
const session = require('koa-session-minimal')
const MysqlStore = require('koa-mysql-session')

const config = require('./../config')

// code ...

const app = new Koa()

// session存储配置
const sessionMysqlConfig= {
  user: config.database.USERNAME,
  password: config.database.PASSWORD,
  database: config.database.DATABASE,
  host: config.database.HOST,
}

// 配置session中间件
app.use(session({
  key: 'USER_SID',
  store: new MysqlStore(sessionMysqlConfig)
}))
// code ...
@前端进阶之旅: 代码已经复制到剪贴板

面试官追问

追问 1同事说这样配会把整个用户 session 写进 Cookie,对吗?
参考回答

不对。配了 store 之后 Cookie 里只有 USER_SID=随机id,数据存在 MySQL 的会话表里。在 DevTools 的 Application 面板看 Cookie 值,再去库里按这个 id 查一下就清楚了。

追问 2session 中间件写在路由后面,会出现什么现象?
参考回答

路由执行时 ctx.session 还不存在,读出来是 undefined,写进去也不会被保存,表现就是登录后永远是未登录。Koa 中间件按 app.use 的顺序执行,session 要放在 router.routes() 前面。

追问 3服务部署了三台,session 存在进程内存里,会怎样?
参考回答

负载均衡把请求随机分到三台机器,登录时写在 A 机,下一个请求到了 B 机就查不到,用户随机掉线。要么用 MySQL / Redis 共享存储,要么在负载均衡上做会话粘滞,前者更靠谱。

追问 4数据库配置读不到时,想默认连本机 MySQL 继续启动,可以吗?
参考回答

我不同意。会话可能写到一个错的库里,服务看着启动成功,用户却一直登不上,排查更费劲。关键配置缺失就让进程启动时直接报错退出,比带病运行好查得多。

追问 5线上所有用户间歇性被踢下线,日志里有 session 存储连接报错,先看什么?
参考回答

先看 MySQL 连接数是不是打满了、有没有慢查询拖住连接池,会话表没清理过期数据也会越来越慢。确认后调连接池大小、给会话表加过期清理,长期看把 session 挪到 Redis

基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket
完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

session登录态判断处理|Nodejs专题系列

# session登录态判断处理

30 秒速记

  • session = 服务端存用户状态,浏览器 Cookie 里只放一个 session id,每次请求带上它来认人
  • 有 Cookie 不等于已登录:要拿 id 去存储里查到会话、没过期、里面有 userId
  • 登录态校验写成中间件,通过后把用户放进 ctx.state.user,后面直接用
  • 公开页、可选登录页、必须登录页分开挂,别全局一刀切
  • 页面没登录 → 跳登录页;API 没登录 → 401,登录了但没权限 → 403

session 登录态的原理是:用户状态存在服务端,浏览器只拿一个 session id 放在 Cookie 里,每次请求服务端拿这个 id 去查是谁。 判断登录态时只看浏览器有没有这个 Cookie 是不够的,要真的去存储里查到会话,确认没过期、有 userId,比较严格的场景还要确认账号没被禁用。这段逻辑我会写成一个中间件,只挂在需要登录的路由上,通过后把用户信息放进 ctx.state.user;页面请求失败就跳登录页,接口请求失败就返回 401。

理解「session登录态判断处理」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 security 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

HTTP 本身是无状态的,服务端分不清两次请求是不是同一个人发的。session 的思路是让服务端记账:用户登录成功后,服务端生成一个随机的 session id,把「这个 id 对应用户 42,已登录」存进内存、MySQL 或者 Redis,再通过 Set-Cookie 把 id 发给浏览器。之后浏览器每次请求自动带上这个 Cookie,服务端拿 id 一查就知道是谁。

一个能用的登录校验中间件大概长这样:

// middlewares/requireLogin.js
module.exports = function requireLogin({ api = false } = {}) {
  return async (ctx, next) => {
    const s = ctx.session
    if (!s || !s.isLogin || !s.userId) {
      if (api) {
        ctx.status = 401
        ctx.body = { success: false, code: 'NOT_LOGIN' }
      } else {
        ctx.redirect('/login?returnTo=' + encodeURIComponent(ctx.path))
      }
      return
    }
    ctx.state.user = { id: s.userId, name: s.userName }
    await next()
  }
}

// 只挂在需要登录的路由上
router.get('/work', requireLogin(), pageCtrl.work)
router.get('/user/getUserInfo.json', requireLogin({ api: true }), userCtrl.getLoginUserInfo)
@前端进阶之旅: 代码已经复制到剪贴板

几个容易忽略的点:session 中间件要先于这个中间件注册,否则 ctx.session 还没恢复;Cookie 要设 httpOnly,防止被页面脚本读走;returnTo 跳回时只允许站内路径,不然就是开放重定向漏洞。

面试官追问

追问 1只要请求带了 USER_SID 这个 Cookie 就认为已登录,有什么问题?
参考回答

Cookie 是客户端随便能改的,随便填一个值也能带上。必须拿这个 id 去存储里查,查不到、过期了、里面没有 userId 都算没登录。

追问 2用户改了密码,其他设备上的旧登录要立刻失效,session 方案怎么做?
参考回答

在用户表里加一个 tokenVersion,写 session 时一起存进去,校验中间件每次比对数据库里的版本,对不上就踢掉。或者改密码时直接把这个用户所有会话从存储里删掉,前提是存储能按 userId 查会话。

追问 3session 和 JWT 怎么选?
参考回答

session 状态在服务端,踢人、改权限立刻生效,代价是要有共享存储;JWT 无状态、扩展方便,但签发出去就收不回来,只能靠短有效期加刷新令牌。传统网站、后台系统我一般用 session,开放给第三方或者跨很多服务的场景再考虑 JWT。

追问 4App 客户端存不住 Cookie,还能用这套 session 吗?
参考回答

可以,把 session id 放进请求头比如 Authorization: Bearer <sid>,服务端从头里取再去存储查,逻辑不变。要注意头和 Cookie 两个来源别混着判断,同一个接口只认一种。

追问 5登录后一刷新就掉线,按什么顺序查?
参考回答

先看登录响应有没有 Set-Cookie,再看刷新请求有没有带上这个 Cookie,最后看服务端拿这个 id 能不能查到会话。三步哪步断了就在哪查:没下发看中间件顺序,没带上看 Domain、Path、SameSite、Secure,查不到看存储。

# session登录态判断处理

# 使用session中间件

30 秒速记

  • koa-session-minimal 提供 ctx.session,koa-mysql-session 把会话数据存进 MySQL
  • key: 'USER_SID' 是 Cookie 名,Cookie 里只有 session id,用户数据在 MySQL 里
  • session 中间件必须在所有读写 ctx.session 的中间件和路由之前注册
  • 多实例部署要共享存储(MySQL / Redis),存内存的话请求打到另一台就掉线
  • Cookie 记得配 httpOnly、sameSite,HTTPS 下加 secure

这段配置做的事是:用 koa-session-minimal 给每个请求挂上 ctx.session,用 koa-mysql-session 把会话内容存到 MySQL,浏览器 Cookie 里只放一个叫 USER_SID 的 id。 存储放在 MySQL 而不是进程内存,是为了服务重启或者多台机器部署时登录态不丢。注册顺序很关键,session 中间件要放在路由前面,后面的代码才拿得到恢复好的 ctx.session。实际项目里 session 我更多放 Redis,读写快、自带过期,MySQL 存也能用,就是要定期清理过期记录。

// code ...
const session = require('koa-session-minimal')
const MysqlStore = require('koa-mysql-session')

const config = require('./../config')

// code ...

const app = new Koa()

// session存储配置
const sessionMysqlConfig= {
  user: config.database.USERNAME,
  password: config.database.PASSWORD,
  database: config.database.DATABASE,
  host: config.database.HOST,
}

// 配置session中间件
app.use(session({
  key: 'USER_SID',
  store: new MysqlStore(sessionMysqlConfig)
}))
// code ...
@前端进阶之旅: 代码已经复制到剪贴板

面试官追问

追问 1同事说这样配会把整个用户 session 写进 Cookie,对吗?
参考回答

不对。配了 store 之后 Cookie 里只有 USER_SID=随机id,数据存在 MySQL 的会话表里。在 DevTools 的 Application 面板看 Cookie 值,再去库里按这个 id 查一下就清楚了。

追问 2session 中间件写在路由后面,会出现什么现象?
参考回答

路由执行时 ctx.session 还不存在,读出来是 undefined,写进去也不会被保存,表现就是登录后永远是未登录。Koa 中间件按 app.use 的顺序执行,session 要放在 router.routes() 前面。

追问 3服务部署了三台,session 存在进程内存里,会怎样?
参考回答

负载均衡把请求随机分到三台机器,登录时写在 A 机,下一个请求到了 B 机就查不到,用户随机掉线。要么用 MySQL / Redis 共享存储,要么在负载均衡上做会话粘滞,前者更靠谱。

追问 4数据库配置读不到时,想默认连本机 MySQL 继续启动,可以吗?
参考回答

我不同意。会话可能写到一个错的库里,服务看着启动成功,用户却一直登不上,排查更费劲。关键配置缺失就让进程启动时直接报错退出,比带病运行好查得多。

追问 5线上所有用户间歇性被踢下线,日志里有 session 存储连接报错,先看什么?
参考回答

先看 MySQL 连接数是不是打满了、有没有慢查询拖住连接池,会话表没清理过期数据也会越来越慢。确认后调连接池大小、给会话表加过期清理,长期看把 session 挪到 Redis。

# 登录成功后设置session到MySQL和设置sessionId到cookie

30 秒速记

  • 登录成功后往 ctx.session 上挂 isLogin、userName、userId,中间件在响应时自动存库并下发 Cookie
  • userId 才是认人的依据;isLogin 只是标记,userName 只是展示用
  • 写之前先换新 session id,防会话固定攻击
  • session 里只放少量不常变的字段,昵称、权限这类易变数据按 userId 查
  • 退出登录要 ctx.session = null 整个销毁,只改 isLogin = false 不够

登录成功后,只要往 ctx.session 上赋几个值,session 中间件就会在响应阶段把它存进 MySQL,并通过 Set-Cookie 把 session id 发给浏览器。 这里写了三个字段,真正有用的是 userId,后面查用户、做权限都靠它,isLogin 只是个标记,userName 方便页面直接展示。我会在写这几个值之前先轮换一次 session id;退出时直接把整个 session 置空,而不是只把 isLogin 改成 false,不然 userId 还留在会话里,哪个接口只判断 userId 就会漏。

let session = ctx.session
session.isLogin = true
session.userName = userResult.name
session.userId = userResult.id
@前端进阶之旅: 代码已经复制到剪贴板

← 12.8 登录注册功能实现13.1 import export使用 →

fe